/*
 * Copyright 2013 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.workbench.models.guided.scorecard.backend;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.dmg.pmml.pmml_4_2.descr.Attribute;
import org.dmg.pmml.pmml_4_2.descr.Characteristic;
import org.dmg.pmml.pmml_4_2.descr.Characteristics;
import org.dmg.pmml.pmml_4_2.descr.Extension;
import org.dmg.pmml.pmml_4_2.descr.FIELDUSAGETYPE;
import org.dmg.pmml.pmml_4_2.descr.INVALIDVALUETREATMENTMETHOD;
import org.dmg.pmml.pmml_4_2.descr.MiningField;
import org.dmg.pmml.pmml_4_2.descr.MiningSchema;
import org.dmg.pmml.pmml_4_2.descr.Output;
import org.dmg.pmml.pmml_4_2.descr.PMML;
import org.dmg.pmml.pmml_4_2.descr.Scorecard;
import org.drools.core.util.StringUtils;
import org.drools.scorecards.ScorecardCompiler;
import org.drools.scorecards.parser.xls.XLSKeywords;
import org.drools.scorecards.pmml.ScorecardPMMLExtensionNames;
import org.drools.scorecards.pmml.ScorecardPMMLGenerator;
import org.drools.scorecards.pmml.ScorecardPMMLUtils;
import org.drools.workbench.models.guided.scorecard.shared.ScoreCardModel;
import org.kie.pmml.pmml_4_2.extensions.PMMLExtensionNames;
import org.kie.soup.project.datamodel.imports.Import;

public class GuidedScoreCardDRLPersistence {

    private static final List<String> NUMERIC_OPERATORS = Arrays.asList("=", ">", "<", ">=", "<=");

    public static String marshal(final ScoreCardModel model) {
        final PMML pmml = createPMMLDocument(model);

        final StringBuilder sb = new StringBuilder();

        //Package statement and Imports are appended by org.drools.scorecards.drl.AbstractDRLEmitter

        //Build rules
        sb.append(ScorecardCompiler.convertToDRL(pmml,
                                                 ScorecardCompiler.DrlType.EXTERNAL_OBJECT_MODEL));

        return sb.toString();
    }

    public static PMML createPMMLDocument(final ScoreCardModel model) {

        final GuidedScoreCardModelVisitor visitor = new GuidedScoreCardModelVisitor(model);
        for (String imported : visitor.getConsumedPackageImports()) {
            Import anImport = new Import(imported);
            if(!model.getImports().contains(anImport)){
                model.getImports().addImport(anImport);
            }
        }

        final Scorecard pmmlScorecard = ScorecardPMMLUtils.createScorecard();
        final Output output = new Output();
        final Characteristics characteristics = new Characteristics();
        final MiningSchema miningSchema = new MiningSchema();

        Extension extension = new Extension();
        extension.setName(PMMLExtensionNames.EXTERNAL_CLASS);
        extension.setValue(model.getFactName());

        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(extension);

        String agendaGroup = model.getAgendaGroup();
        if (!StringUtils.isEmpty(agendaGroup)) {
            extension = new Extension();
            extension.setName(PMMLExtensionNames.AGENDA_GROUP);
            extension.setValue(agendaGroup);
            pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(extension);
        }

        String ruleFlowGroup = model.getRuleFlowGroup();
        if (!StringUtils.isEmpty(ruleFlowGroup)) {
            extension = new Extension();
            extension.setName(PMMLExtensionNames.RULEFLOW_GROUP);
            extension.setValue(agendaGroup);
            pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(extension);
        }

        extension = new Extension();
        extension.setName(PMMLExtensionNames.MODEL_IMPORTS);
        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(extension);
        List<String> imports = new ArrayList<String>();
        StringBuilder importBuilder = new StringBuilder();
        for (Import imp : model.getImports().getImports()) {
            if (!imports.contains(imp.getType())) {
                imports.add(imp.getType());
                importBuilder.append(imp.getType()).append(",");
            }
        }
        extension.setValue(importBuilder.toString());

        extension = new Extension();
        extension.setName(ScorecardPMMLExtensionNames.SCORECARD_RESULTANT_SCORE_FIELD);
        extension.setValue(model.getFieldName());
        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(extension);

        extension = new Extension();
        extension.setName(PMMLExtensionNames.MODEL_PACKAGE);
        String pkgName = model.getPackageName();
        extension.setValue(!(pkgName == null || pkgName.isEmpty()) ? pkgName : null);
        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(extension);

        final String modelName = convertToJavaIdentifier(model.getName());
        pmmlScorecard.setModelName(modelName);
        pmmlScorecard.setInitialScore(model.getInitialScore());
        pmmlScorecard.setUseReasonCodes(model.isUseReasonCodes());

        if (model.isUseReasonCodes()) {
            pmmlScorecard.setBaselineScore(model.getBaselineScore());
            pmmlScorecard.setReasonCodeAlgorithm(model.getReasonCodesAlgorithm());
        }

        for (final org.drools.workbench.models.guided.scorecard.shared.Characteristic characteristic : model.getCharacteristics()) {
            ImportsToFQCN importsToFQCN = new ImportsToFQCN(model.getImports().getImports());
            final Characteristic _characteristic = new Characteristic();
            characteristics.getCharacteristics().add(_characteristic);

            extension = new Extension();
            extension.setName(PMMLExtensionNames.EXTERNAL_CLASS);
            extension.setValue(importsToFQCN.resolveFQCN(characteristic.getFact()));
            _characteristic.getExtensions().add(extension);

            extension = new Extension();
            extension.setName(ScorecardPMMLExtensionNames.CHARACTERTISTIC_DATATYPE);
            switch (characteristic.getDataType().toLowerCase()) {
                case "string":
                    extension.setValue(XLSKeywords.DATATYPE_TEXT);
                    break;
                case "int":
                    extension.setValue(XLSKeywords.DATATYPE_INTEGER);
                    break;
                case "double":
                    extension.setValue(XLSKeywords.DATATYPE_DOUBLE);
                    break;
                case "boolean":
                    extension.setValue(XLSKeywords.DATATYPE_BOOLEAN);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown datatype found: " + characteristic.getDataType() + "!");
            }
            _characteristic.getExtensions().add(extension);

            _characteristic.setBaselineScore(characteristic.getBaselineScore());
            if (model.isUseReasonCodes()) {
                _characteristic.setReasonCode(characteristic.getReasonCode());
            }
            _characteristic.setName(characteristic.getName());

            final MiningField miningField = new MiningField();
            miningField.setName(characteristic.getField());
            miningField.setUsageType(FIELDUSAGETYPE.ACTIVE);
            miningField.setInvalidValueTreatment(INVALIDVALUETREATMENTMETHOD.RETURN_INVALID);
            miningSchema.getMiningFields().add(miningField);

            extension = new Extension();
            extension.setName(PMMLExtensionNames.EXTERNAL_CLASS);
            extension.setValue(importsToFQCN.resolveFQCN(characteristic.getFact()));
            miningField.getExtensions().add(extension);

            for (final org.drools.workbench.models.guided.scorecard.shared.Attribute attribute : characteristic.getAttributes()) {
                final Attribute _attribute = new Attribute();
                _characteristic.getAttributes().add(_attribute);

                extension = new Extension();
                extension.setName(ScorecardPMMLExtensionNames.CHARACTERTISTIC_FIELD);
                extension.setValue(characteristic.getField());
                _attribute.getExtensions().add(extension);

                if (model.isUseReasonCodes()) {
                    _attribute.setReasonCode(attribute.getReasonCode());
                }
                _attribute.setPartialScore(attribute.getPartialScore());

                final String operator = attribute.getOperator();
                final String dataType = characteristic.getDataType();
                String predicateResolver;
                if ("boolean".equalsIgnoreCase(dataType)) {
                    predicateResolver = operator.toUpperCase();
                } else if ("String".equalsIgnoreCase(dataType)) {
                    if (operator.contains("=")) {
                        predicateResolver = operator + attribute.getValue();
                    } else {
                        predicateResolver = attribute.getValue() + ",";
                    }
                } else {
                    if (NUMERIC_OPERATORS.contains(operator)) {
                        predicateResolver = operator + " " + attribute.getValue();
                    } else {
                        predicateResolver = attribute.getValue().replace(",", "-");
                    }
                }
                extension = new Extension();
                extension.setName("predicateResolver");
                extension.setValue(predicateResolver);
                _attribute.getExtensions().add(extension);
            }
        }

        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(miningSchema);
        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(output);
        pmmlScorecard.getExtensionsAndCharacteristicsAndMiningSchemas().add(characteristics);
        return new ScorecardPMMLGenerator().generateDocument(pmmlScorecard);
    }

    private static String convertToJavaIdentifier(final String modelName) {
        final StringBuilder sb = new StringBuilder();
        if (!Character.isJavaIdentifierStart(modelName.charAt(0))) {
            sb.append("_");
        }
        for (char c : modelName.toCharArray()) {
            if (!Character.isJavaIdentifierPart(c)) {
                sb.append("_");
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}
